package demoMod.stsconsole.rooms;

import demoMod.stsconsole.ConsoleEntry;
import demoMod.stsconsole.cards.AbstractCard;
import demoMod.stsconsole.dungeons.AbstractDungeon;
import demoMod.stsconsole.helpers.CardLibrary;
import demoMod.stsconsole.helpers.LocalizedStrings;
import demoMod.stsconsole.helpers.PotionLibrary;
import demoMod.stsconsole.helpers.RandomSequence;
import demoMod.stsconsole.interfaces.ChangePriceSubscriber;
import demoMod.stsconsole.interfaces.PurchaseSubscriber;
import demoMod.stsconsole.interfaces.Saleable;
import demoMod.stsconsole.potions.AbstractPotion;
import demoMod.stsconsole.relics.AbstractRelic;
import demoMod.stsconsole.relics.Circlet;
import demoMod.stsconsole.utils.CardSelect;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class ShopRoom extends AbstractRoom {
    public static Shop currShop;

    public static PurchaseSubscriber defaultPurchaseLogic = new PurchaseSubscriber() {
        @Override
        public boolean canPurchase(Saleable product, int price) {
            return price <= AbstractDungeon.player.getGold();
        }

        @Override
        public void afterPurchase(Saleable product, int price) {
            if (currShop.coloredCards.contains(product)) {
                currShop.coloredCards.set(currShop.coloredCards.indexOf(product), null);
            }

            if (currShop.colorlessCards.contains(product)) {
                currShop.colorlessCards.set(currShop.colorlessCards.indexOf(product), null);
            }

            if (currShop.relics.contains(product)) {
                currShop.relics.set(currShop.relics.indexOf(product), null);
            }

            if (currShop.potions.contains(product)) {
                currShop.potions.set(currShop.potions.indexOf(product), null);
            }
        }

        @Override
        public int getPriority() {
            return 0;
        }
    };

    @Override
    public void onPlayerEntry() {
        super.onPlayerEntry();
        this.phase = RoomPhase.COMPLETED;
        currShop = new Shop();
        List<String> uiStrings = LocalizedStrings.getUIStrings("ShopRoom");
        AbstractDungeon.player.talk(uiStrings.get(0));
    }

    @Override
    public String getRoomSymbol() {
        return "$";
    }

    public class Shop {
        public int discountIndex;
        public List<AbstractCard> coloredCards = new ArrayList<>();
        public List<AbstractCard> colorlessCards = new ArrayList<>();
        public List<AbstractRelic> relics = new ArrayList<>();
        public List<AbstractPotion> potions = new ArrayList<>();
        public CardPurgeService cardPurgeService;
        public RandomSequence coloredCardAttackRng;
        public RandomSequence coloredCardSkillRng;
        public RandomSequence coloredCardPowerRng;
        public RandomSequence colorlessCardRng;
        public RandomSequence regularRelicRng;
        public RandomSequence shopRelicRng;
        public RandomSequence potionRng;

        public Shop() {
            cardPurgeService = new CardPurgeService();

            coloredCardAttackRng = new RandomSequence(AbstractDungeon.shopRng.nextLong());
            coloredCardSkillRng = new RandomSequence(AbstractDungeon.shopRng.nextLong());
            coloredCardPowerRng = new RandomSequence(AbstractDungeon.shopRng.nextLong());
            colorlessCardRng = new RandomSequence(AbstractDungeon.shopRng.nextLong());
            regularRelicRng = new RandomSequence(AbstractDungeon.shopRng.nextLong());
            shopRelicRng = new RandomSequence(AbstractDungeon.shopRng.nextLong());
            potionRng = new RandomSequence(AbstractDungeon.shopRng.nextLong());

            List<AbstractCard> coloredCards = CardLibrary.getCards(AbstractDungeon.player.getCardColor());
            List<AbstractCard> attackCards = coloredCards.stream().filter(card -> card.type == AbstractCard.CardType.ATTACK).collect(Collectors.toList());
            List<AbstractCard> skillCards = coloredCards.stream().filter(card -> card.type == AbstractCard.CardType.SKILL).collect(Collectors.toList());
            List<AbstractCard> powerCards = coloredCards.stream().filter(card -> card.type == AbstractCard.CardType.POWER).collect(Collectors.toList());
            Collections.shuffle(attackCards, coloredCardAttackRng);
            Collections.shuffle(skillCards, coloredCardSkillRng);
            Collections.shuffle(powerCards, coloredCardPowerRng);

            this.coloredCards.addAll(attackCards.subList(0, 2));
            this.coloredCards.addAll(skillCards.subList(0, 2));
            this.coloredCards.add(powerCards.get(0));

            //TODO 无色牌

            List<AbstractRelic> relics = AbstractDungeon.relicPool.stream().filter(
                    relic -> relic.rarity == AbstractRelic.RelicRarity.COMMON ||
                            relic.rarity == AbstractRelic.RelicRarity.UNCOMMON ||
                            relic.rarity == AbstractRelic.RelicRarity.RARE).collect(Collectors.toList());
            Collections.shuffle(relics, regularRelicRng);
            AbstractRelic.RelicRarity rarity = getRandomRelicRarity(regularRelicRng);
            for (int i=0;i<2;i++) {
                boolean found = false;
                while (rarity.ordinal() > 0) {
                    AbstractRelic.RelicRarity tmpRarity = rarity;
                    Optional<AbstractRelic> tmpRelic = relics.stream().filter(relic -> relic.rarity == tmpRarity).findFirst();
                    if (tmpRelic.isPresent()) {
                        found = true;
                        this.relics.add(tmpRelic.get());
                        relics.remove(tmpRelic.get());
                        AbstractDungeon.relicPool.remove(tmpRelic.get());
                        break;
                    }
                    rarity = AbstractRelic.RelicRarity.values()[rarity.ordinal() - 1];
                }
                rarity = getRandomRelicRarity(regularRelicRng);
                if (!found) {
                    this.relics.add(new Circlet());
                }
            }
            relics = AbstractDungeon.relicPool.stream().filter(relic -> relic.rarity == AbstractRelic.RelicRarity.SHOP).collect(Collectors.toList());
            Collections.shuffle(relics, shopRelicRng);
            if (!relics.isEmpty()) {
                this.relics.add(relics.remove(0));
                AbstractDungeon.relicPool.remove(this.relics.get(0));
            } else {
                this.relics.add(new Circlet());
            }

            AbstractPotion.PotionRarity potionRarity = getRandomPotionRarity(potionRng);
            for (int i=0;i<3;i++) {
                AbstractPotion potion = PotionLibrary.getPotion(potionRng, potionRarity);
                this.potions.add(potion);
            }

            this.coloredCards.forEach(card -> {
                card.isPriceInitialized = false;
                card.calculatePrice(AbstractDungeon.shopRng);
            });
            this.colorlessCards.forEach(card -> {
                card.isPriceInitialized = false;
                card.calculatePrice(AbstractDungeon.shopRng);
            });
            this.relics.forEach(relic -> relic.calculatePrice(AbstractDungeon.shopRng));
            this.potions.forEach(potion -> potion.calculatePrice(AbstractDungeon.shopRng));
            this.cardPurgeService.calculatePrice(null);
            this.discountIndex = AbstractDungeon.shopRng.nextInt(this.coloredCards.size());
            AbstractCard card = this.coloredCards.get(discountIndex);
            ConsoleEntry.triggerSubscribers(
                    ChangePriceSubscriber.class,
                    "onChangePrice",
                    new Class[]{Saleable.class, int.class, RandomSequence.class},
                    new Object[]{card, card.getPrice() / 2, AbstractDungeon.shopRng},
                    (method, params, subscriber) -> {
                        if (params[0] == card) {
                            try {
                                params[1] = method.invoke(subscriber, params);
                            } catch (IllegalAccessException | InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    }
            );
        }
    }

    public static class CardPurgeService implements Saleable, ChangePriceSubscriber, PurchaseSubscriber {
        public boolean purchased = false;
        public int price;

        @Override
        public void calculatePrice(RandomSequence rng) {
            this.price = getBasePrice();
            ConsoleEntry.triggerSubscribers(
                    ChangePriceSubscriber.class,
                    "onChangePrice",
                    new Class[]{Saleable.class, int.class, RandomSequence.class},
                    new Object[]{this, this.price, rng},
                    (method, params, subscriber) -> {
                        if (params[0] == this) {
                            try {
                                params[1] = method.invoke(subscriber, params);
                                this.price = (int) params[1];
                            } catch (IllegalAccessException | InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    }
            );
        }

        @Override
        public int getBasePrice() {
            return 75;
        }

        @Override
        public int getPrice() {
            return this.price;
        }

        @Override
        public void purchase() {
            List<AbstractCard> cards = CardSelect.selectCards(AbstractDungeon.player.masterDeck, 1, card -> card.canRemoveFromMasterDeck, false);
            if (!cards.isEmpty()) {
                cards.get(0).onRemove();
                AbstractDungeon.player.removeCardFromMasterDeck(cards.get(0));
                AbstractDungeon.player.cardPurgeServiceCount++;
            }
        }

        @Override
        public int onChangePrice(Saleable product, int price, RandomSequence rng) {
            if (product == this) {
                price += 25 * AbstractDungeon.player.cardPurgeServiceCount;
            }
            return price;
        }

        @Override
        public boolean canPurchase(Saleable product, int price) {
            return !purchased;
        }

        @Override
        public void afterPurchase(Saleable product, int price) {
            purchased = true;
        }
    }
}
